www.gusucode.com > MyGosuMenu 1.5.8源码程序 > MyGosuMenu 1.5.8/MyGosuMenu-1.5.8/DynamicTree/DynamicTreeBuilder.js
/* * DO NOT REMOVE THIS NOTICE * * PROJECT: MyGosuMenu * VERSION: 1.5.5 * COPYRIGHT: (c) 2003-2009 Cezary Tomczak * LINK: http://www.gosu.pl/MyGosuMenu/ * LICENSE: BSD revised (free for any use) */ function DynamicTreeBuilder(id) { this.path = "images/"; this.img = { "branch": "tree-branch.gif", "doc": "tree-doc.gif", "folder": "tree-folder.gif", "folderOpen": "tree-folder-open.gif", "leaf": "tree-leaf.gif", "leafEnd": "tree-leaf-end.gif", "node": "tree-node.gif", "nodeEnd": "tree-node-end.gif", "nodeOpen": "tree-node-open.gif", "nodeOpenEnd": "tree-node-open-end.gif" }; this.cookiePath = ""; this.cookieDomain = ""; this.init = function() { var p, img; for (p in this.img) { this.img[p] = this.path + this.img[p]; } for (p in this.img) { this.imgObjects.push(new Image()); this.imgObjects.getLast().src = this.img[p]; this.img[p] = this.imgObjects.getLast().src; } this.parse(document.getElementById(this.id).childNodes, this.tree); this.loadState(); if (window.addEventListener) { window.addEventListener("unload", function(e) { self.saveState(); }, false); } else if (window.attachEvent) { window.attachEvent("onunload", function(e) { self.saveState(); }); } this.updateHtml(); }; this.reset = function() { this.clearState(); this.tree = new Node("tree", "", null, new Array(), false, true); this.allNodes = {}; this.opened = []; this.active = ""; this.count = 0; this.parse(document.getElementById(this.id).childNodes, this.tree); this.updateHtml(); }; this.parse = function(nodes, tree) { for (var i = 0; i < nodes.length; i++) { if (nodes[i].nodeType == 1) { if (!nodes[i].className) { continue; } nodes[i].id = this.id + "-" + (++this.count); var node = new Node(); node.id = nodes[i].id; if (nodes[i].firstChild) { // whitespace characters before Anchor, check for: doc, folder, test on all browsers //if (nodes[i].className == "doc" && nodes[i].firstChild.nodeType != 1) { nodes[i].removeChild(nodes[i].firstChild); } if (nodes[i].firstChild.tagName == "A") { var a = nodes[i].firstChild; if (a.firstChild) { node.text = a.firstChild.nodeValue.trim(); } if (a.href) { // dirty hack for ie (automatic conversion to absolute paths problem), see also DynamicTreePlugins.importFromHtml() var s = a.parentNode.innerHTML.match(/href=["'](dynamictree:\/\/dynamictree\/)?([^"']*)["']/i); if (s) { node.href = s[2]; } } if (a.title) { node.title = a.title; } if (a.target) { node.target = a.target; } } else { node.text = nodes[i].firstChild.nodeValue.trim(); } } node.parentNode = tree; node.childNodes = (nodes[i].className == "folder" ? new Array() : null); node.isDoc = (nodes[i].className == "doc"); node.isFolder = (nodes[i].className == "folder"); tree.childNodes.push(node); this.allNodes[node.id] = node; } if (nodes[i].nodeType == 1 && nodes[i].childNodes) { this.parse(nodes[i].childNodes, tree.childNodes.getLast()); } } }; this.nodeClick = function(id) { var el = document.getElementById(id+"-section"); var node = document.getElementById(id+"-node"); var icon = document.getElementById(id+"-icon"); if (el.style.display == "block") { el.style.display = "none"; if (this.allNodes[id].isLast()) { node.src = this.img.nodeEnd; } else { node.src = this.img.node; } icon.src = this.img.folder; this.opened.removeByValue(id); } else { el.style.display = "block"; if (this.allNodes[id].isLast()) { node.src = this.img.nodeOpenEnd; } else { node.src = this.img.nodeOpen; } icon.src = this.img.folderOpen; this.opened.push(id); } /* fix ie bug - images not showing */ if (node.outerHTML) { node.outerHTML = node.outerHTML; } if (icon.outerHTML) { icon.outerHTML = icon.outerHTML; } }; this.textClick = function(id) { if (this.active) { document.getElementById(this.active+"-text").className = "text"; } document.getElementById(id+"-text").className = "text-active"; this.active = id; this.textClickListener.call(); }; this.toHtml = function() { var s = ""; var nodes = this.tree.childNodes; for (var i = 0; i < nodes.length; i++) { s += nodes[i].toHtml(); } return s; }; this.updateHtml = function() { document.getElementById(this.id).innerHTML = this.toHtml(); }; this.loadState = function() { var opened = this.cookie.get("opened"); if (opened) { this.opened = opened.split("|"); this.opened.filter(function(id) { return self.allNodes[id] && self.allNodes[id].isFolder && self.allNodes[id].childNodes.length; }); } }; this.saveState = function() { if (this.opened.length) { this.cookie.set("opened", this.opened.join("|"), 3600*24*30, this.cookiePath, this.cookieDomain); } else { this.clearState(); } }; this.clearState = function() { this.cookie.del("opened"); }; this.getActiveNode = function() { if (!this.active) { throw "DynamicTreeBuilder.getActiveNode() failed, there is no active node"; } return this.allNodes[this.active]; } this.mayMoveUp = function() { return this.active && !this.allNodes[this.active].isFirst(); }; this.mayMoveDown = function() { return this.active && !this.allNodes[this.active].isLast(); }; this.mayMoveLeft = function() { return this.active && (this.allNodes[this.active].getLevel() > 1); }; this.mayMoveRight = function() { if (this.active) { var node = this.allNodes[this.active].getNextSibling(); while (node) { if (node.isFolder) { return true; } node = node.getNextSibling(); } } return false; }; this.mayInsertBefore = function() { return Boolean(this.active); }; this.mayInsertAfter = function() { return Boolean(this.active); }; this.mayInsertInside = function() { return this.active && this.allNodes[this.active].isFolder; }; this.mayRemove = function() { if (this.active) { var node = this.allNodes[this.active]; if (node.isDoc) { return true; } if (node.isFolder && !node.childNodes.length) { return true; } } return false; }; this.moveUp = function() { var node = this.allNodes[this.active]; var index = node.getIndex(); var parent = node.parentNode; parent.removeChild(node); parent.appendChildAtIndex(node, index-1); this.updateHtml(); }; this.moveDown = function() { var node = this.allNodes[this.active]; var index = node.getIndex(); var parent = node.parentNode; parent.removeChild(node); parent.appendChildAtIndex(node, index+1); this.updateHtml(); }; this.moveLeft = function() { var node = this.allNodes[this.active]; var left = node.parentNode; left.removeChild(node); left.parentNode.appendChildAtIndex(node, left.getIndex()); this.updateHtml(); }; this.moveRight = function() { var node = this.allNodes[this.active]; var next = node.getNextSibling(); var rightId = null; while (next) { if (next.isFolder) { rightId = next.id; break; } next = next.getNextSibling(); } var right = this.allNodes[rightId]; node.parentNode.removeChild(node); if (right.childNodes.length) { right.appendChildAtIndex(node, 0); } else { right.appendChild(node); } this.updateHtml(); }; this.createNode = function(id, text, type, object) { if (!id || this.allNodes[id] || !text || (type != "doc" && type != "folder")) { throw this.id+'.createNode("'+id+'", "'+text+'", "'+type+'") failed, illegal action'; } var node; if (type == "doc") { node = new Node(id, text, null, null, true, false); } else { node = new Node(id, text, null, new Array(), false, true); } if (object) { for (var p in object) { node[p] = object[p]; } } this.allNodes[id] = node; return node; }; this.insert = function(id, text, type, object) { var node = this.createNode(id, text, type, object); if (this.tree.childNodes.length) { this.tree.appendChildAtIndex(node, 0); } else { this.tree.appendChild(node); } this.updateHtml(); }; this.insertBefore = function(id, text, type, object) { if (!this.mayInsertBefore()) { throw this.id+'.insertBefore() failed, illegal action'; } var node = this.createNode(id, text, type, object); var active = this.allNodes[this.active]; active.parentNode.appendChildAtIndex(node, active.getIndex()); this.updateHtml(); }; this.insertAfter = function(id, text, type, object) { if (!this.mayInsertAfter()) { throw this.id+'.insertAfter() failed, illegal action'; } var node = this.createNode(id, text, type, object); var active = this.allNodes[this.active]; if (active.parentNode.childNodes[active.getIndex()+1]) { active.parentNode.appendChildAtIndex(node, active.getIndex()+1); } else { active.parentNode.appendChild(node); } this.updateHtml(); }; this.insertInsideAtStart = function(id, text, type, object) { if (!this.mayInsertInside()) { throw this.id+'.insertInsideAtStart() failed, illegal action'; } var node = this.createNode(id, text, type, object); var active = this.allNodes[this.active]; if (active.childNodes.length) { active.appendChildAtIndex(node, 0); } else { active.appendChild(node); } this.updateHtml(); }; this.insertInsideAtEnd = function(id, text, type, object) { if (!this.mayInsertInside()) { throw this.id+'.insertInsideAtEnd() failed, illegal action'; } var node = this.createNode(id, text, type, object); var active = this.allNodes[this.active]; active.appendChild(node); this.updateHtml(); }; this.remove = function() { var node = this.allNodes[this.active]; node.parentNode.removeChild(node); this.allNodes[this.active] = null; this.active = ""; this.updateHtml(); }; function Node(id, text, parentNode, childNodes, isDoc, isFolder) { this.id = id; this.text = text; this.parentNode = parentNode; this.childNodes = childNodes; this.isDoc = isDoc; this.isFolder = isFolder; this.href = ""; this.title = ""; this.target = ""; this.isFirst = function() { if (this.parentNode) { return this.parentNode.childNodes[0].id == this.id; } throw "DynamicTreeBuilder.Node.isFirst() failed, this func cannot be called for the root element"; }; this.isLast = function() { if (this.parentNode) { return this.parentNode.childNodes.getLast().id == this.id; } throw "DynamicTreeBuilder.Node.isLast() failed, this func cannot be called for the root element"; }; this.getLevel = function() { var level = 0; var node = this; while (node.parentNode) { level++; node = node.parentNode; } return level; }; this.getNextSibling = function() { if (this.parentNode) { var nodes = this.parentNode.childNodes; var start = false; for (var i = 0; i < nodes.length; i++) { if (start) { return nodes[i]; } if (!start && this.id != nodes[i].id) { continue; } start = true; } return false; } throw "DynamicTreeBuilder.Node.getNextSibling() failed, this func cannot be called for the root element"; }; this.getPreviousSibling = function() { if (this.parentNode) { var nodes = this.parentNode.childNodes; for (var i = 0; i < nodes.length; i++) { if (nodes[i].id == this.id) { if (i) { return nodes[i-1]; } else { return false; } } } throw "DynamicTreeBuilder.Node.getPreviousSibling() failed, unknown error"; } throw "DynamicTreeBuilder.Node.getPreviousSibling() failed, this func cannot be called for the root element"; }; this.getIndex = function() { if (this.parentNode) { var nodes = this.parentNode.childNodes; for (var i = 0; i < nodes.length; i++) { if (nodes[i].id == this.id) { return i; } } throw "DynamicTreeBuilder.Node.getIndex() failed, unknown error"; } throw "DynamicTreeBuilder.Node.getIndex() failed, this func cannot be called for the root element"; }; this.removeChild = function(node) { this.childNodes.removeByIndex(node.getIndex()); node.parentNode = null; }; this.appendChild = function(node) { this.childNodes.push(node); node.parentNode = this; }; this.appendChildAtIndex = function(node, index) { this.childNodes.pushAtIndex(node, index); node.parentNode = this; }; this.toHtml = function() { var s = '<div class="?" id="?">'.format((this.isFolder ? "folder" : "doc"), this.id); if (this.isFolder) { var nodeIcon; if (this.childNodes.length) { nodeIcon = (self.opened.contains(this.id) ? (this.isLast() ? self.img.nodeOpenEnd : self.img.nodeOpen) : (this.isLast() ? self.img.nodeEnd : self.img.node)); } else { nodeIcon = (this.isLast() ? self.img.leafEnd : self.img.leaf); } var icon = ((self.opened.contains(this.id) && this.childNodes.length) ? self.img.folderOpen : self.img.folder); if (this.childNodes.length) { s += '<a href="javascript:void(0)" onclick="?.nodeClick(\'?\')">'.format(self.id, this.id); } s += '<img id="?-node" src="?" width="18" height="18" alt="" />'.format(this.id, nodeIcon); if (this.childNodes.length) { s += '</a>'; } s += '<img id="?-icon" src="?" width="18" height="18" alt="" />'.format(this.id, icon); s += '<span id="?-text" class="text?" onclick="?.textClick(\'?\')">?</span>'.format(this.id, (self.active == this.id ? '-active' : ''), self.id, this.id, this.text); if (this.childNodes.length) { s += '<div class="section?" id="?-section"'.format((this.isLast() ? " last" : ""), this.id); if (self.opened.contains(this.id)) { s += ' style="display: block;"'; } s += '>'; for (var i = 0; i < this.childNodes.length; i++) { s += this.childNodes[i].toHtml(); } s += '</div>'; } } if (this.isDoc) { s += '<img src="?" width="18" height="18" alt="" /><img src="?" />'.format((this.isLast() ? self.img.leafEnd : self.img.leaf), self.img.doc); s += '<span id="?-text" class="text?" onclick="?.textClick(\'?\')">?</span>'.format(this.id, (self.active == this.id ? '-active' : ''), self.id, this.id, this.text); } s += '</div>'; return s; }; } function Cookie() { this.get = function(name) { var cookies = document.cookie.split(";"); for (var i = 0; i < cookies.length; ++i) { var a = cookies[i].split("="); if (a.length == 2) { a[0] = a[0].trim(); a[1] = a[1].trim(); if (a[0] == name) { return unescape(a[1]); } } } return ""; }; this.set = function(name, value, seconds, path, domain, secure) { var cookie = (name + "=" + escape(value)); if (seconds) { var date = new Date(new Date().getTime()+seconds*1000); cookie += ("; expires="+date.toGMTString()); } cookie += (path ? "; path="+path : ""); cookie += (domain ? "; domain="+domain : ""); cookie += (secure ? "; secure" : ""); document.cookie = cookie; }; this.del = function(name) { document.cookie = name + "=; expires=Thu, 01-Jan-70 00:00:01 GMT"; }; } function Listener() { this.funcs = []; this.add = function(func) { this.funcs.push(func); }; this.call = function() { for (var i = 0; i < this.funcs.length; i++) { this.funcs[i](); } }; } var self = this; this.id = id; this.tree = new Node("tree", "", null, new Array(), false, true); this.allNodes = {}; // id => object this.opened = []; // opened folders this.active = ""; // active node, text clicked this.cookie = new Cookie(); this.imgObjects = []; this.count = 0; this.textClickListener = new Listener(); // other modules also may need to know when user clicks on text } /* Check whether array contains given string */ if (!Array.prototype.contains) { Array.prototype.contains = function(s) { for (var i = 0; i < this.length; ++i) { if (this[i] === s) { return true; } } return false; }; } /* Remove elements with such value (mutates) */ if (!Array.prototype.removeByValue) { Array.prototype.removeByValue = function(value) { var i, indexes = []; for (i = 0; i < this.length; ++i) { if (this[i] === value) { indexes.push(i); } } for (i = indexes.length - 1; i >= 0; --i) { this.splice(indexes[i], 1); } }; } /* Remove elements judged 'false' by the passed function (mutates) */ if (!Array.prototype.filter) { Array.prototype.filter = function(func) { var i, indexes = []; for (i = 0; i < this.length; ++i) { if (!func(this[i])) { indexes.push(i); } } for (i = indexes.length - 1; i >= 0; --i) { this.splice(indexes[i], 1); } }; } /* Get the last element from the array */ if (!Array.prototype.getLast) { Array.prototype.getLast = function() { return this[this.length-1]; }; } /* Strip whitespace from the beginning and end of a string */ if (!String.prototype.trim) { String.prototype.trim = function() { return this.replace(/^\s*|\s*$/g, ""); }; } /* Replace ? tokens with variables passed as arguments in a string */ String.prototype.format = function() { if (!arguments.length) { throw "String.format() failed, no arguments passed, this = "+this; } var tokens = this.split("?"); if (arguments.length != (tokens.length - 1)) { throw "String.format() failed, tokens != arguments, this = "+this; } var s = tokens[0]; for (var i = 0; i < arguments.length; ++i) { s += (arguments[i] + tokens[i + 1]); } return s; }; /* Remove element with given index (mutates) */ if (!Array.prototype.removeByIndex) { Array.prototype.removeByIndex = function(index) { this.splice(index, 1); }; } /* Push an element at specified index */ if (!Array.prototype.pushAtIndex) { Array.prototype.pushAtIndex = function(el, index) { this.splice(index, 0, el); }; }